package com.baseframe.gateway.filter;

import com.baseframe.auth.entity.UserBean;
import com.baseframe.auth.props.AuthProvider;
import com.baseframe.auth.props.BaseAuthProperties;
import com.baseframe.auth.utils.TokenUtil;
import com.baseframe.jwt.props.BaseJwtProperties;
import com.baseframe.jwt.utils.JwtUtil;
import com.baseframe.tool.result.R;
import com.baseframe.tool.utils.data.StringUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;


@Component
@RequiredArgsConstructor
@Slf4j
public class BaseAuthFilter implements GlobalFilter, Ordered {

    private final BaseAuthProperties securityProperties;
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    private final BaseJwtProperties baseJwtProperties;
    private final ObjectMapper objectMapper;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String path = request.getURI().getPath();
        log.info("\npath:{}",path);

        if (!isSkip(path)){
            //1.校验是否携带了请求头或请求参数
            String headerAuth = request.getHeaders().getFirst(TokenUtil.AUTHORIZATION);
            String paramsAuth = request.getQueryParams().getFirst(TokenUtil.AUTHORIZATION);
            if (StringUtils.isBlank(headerAuth) && StringUtils.isBlank(paramsAuth)) {
                return unAuth(response, "缺失令牌,鉴权失败~");
            }
            String auth = StringUtil.isBlank(headerAuth)?paramsAuth:headerAuth;
            String token = TokenUtil.stripOffPrefixes(auth);
            //2.判断令牌是否合法
            UserBean userBean = TokenUtil.parse(token);
            if (userBean==null){
                return unAuth(response, "请求未授权~");
            }
            //3.校验令牌是否过期或合法
            Boolean single = baseJwtProperties.getSingle();
            if (single){
                Long userId = userBean.getUserId();
                String accessToken = TokenUtil.getAccessToken(userId);
                if (!token.equals(accessToken)){
                    return unAuth(response, "令牌已失效~");
                }

            }else {
                String jwtId = JwtUtil.getJwtId(token);
                String accessToken = TokenUtil.getAccessToken(jwtId);
                if (!token.equals(accessToken)){
                    return unAuth(response, "令牌已失效~");
                }

            }

        }


        return chain.filter(exchange);
    }

    private Mono<Void> unAuth(ServerHttpResponse resp, String msg) {
        resp.setStatusCode(HttpStatus.UNAUTHORIZED);
        resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        String result = "";
        try {
            result = objectMapper.writeValueAsString(R.fail(HttpStatus.UNAUTHORIZED.value(),msg));
        } catch (JsonProcessingException e) {
            log.error(e.getMessage(), e);
        }
        DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8));
        return resp.writeWith(Flux.just(buffer));
    }
    private boolean isSkip(String path) {
        return AuthProvider.getDefaultSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path))
                || securityProperties.getSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path));
    }

    @Override
    public int getOrder() {
        return -1;
    }
}
